var _gl = require('opengl');
var _MeshNode = require('drawable/meshnode.js');
var _Container = require('component/container.js');
var _Outline = require('scenes/game/outline.js');
var _Grid = require('scenes/game/gridbg.js');
var _inherit = require('core/inherit.js');

var _global = require('game/global.js');
var R = require('game/texture.js').R;

// ==========================
// Mesh Drawable
// ==========================
/**
 * @param material
 * @param frame
 * @param size
 * @param count point count
 * @constructor
 */
function MapMesh(mesh, material) {
    _MeshNode.call(this, mesh, material);
    this.mCursor = 0;
}
_inherit(MapMesh, _MeshNode);
MapMesh.prototype.getCursor = function () {
    return this.mCursor;
};
MapMesh.prototype.setCursor = function (cursor) {
    this.mCursor = cursor;
};
MapMesh.prototype.upload = function () {
    this.mBuffer.compact(this.mCursor);
    this.mBuffer.upload();
};

// ==========================
// Map
// ==========================
/**
 * left : 0
 * top : 1
 * rigth : 2
 * bottom : 3
 *
 * @param conf
 * @constructor
 */
function Map(conf) {
    _Container.call(this);

    this.mConfig = conf;
    this.mStartEdge = 0;
    this.mCell = 0;// current cell
    this.mEdge = 0;// current edge
    this.setSize(conf.getSize());
    this.initEdgePosition();

    var lineImg;

    // bg mesh
    lineImg = _global.spriteFrame(R.game.map.grids);
    this.mGridModel = new _Grid(lineImg);
    this.mBg = new MapMesh(this.mGridModel.createGLBuffer(conf.visiableCell() * 6), _global.material.positionTexture(lineImg));
    this.mBg.setSize(conf.getSize());
    this.addChild(this.mBg);
    this.fillBg();

    // outline mesh
    lineImg = _global.spriteFrame(R.game.map.outline);
    this.mOutline = new _Outline(lineImg);
    this.mLines = new MapMesh(this.mOutline.createGLBuffer((conf.getCol() + conf.getRow()) * 4 * 12), _global.material.positionTexture(lineImg));
    this.mLines.setSize(conf.getSize());
    this.addChild(this.mLines);
    this.fillLines();
}
_inherit(Map, _Container);
Map.prototype.fillBg = function () {
    var row = this.mConfig.getRow();
    var col = this.mConfig.getCol();

    for (var r = 1, endr = row - 1; r < endr; r++) {
        // iterator all light cell
        for (var c = 1 + (r % 2); c < col; c += 2) {
            var cell = r * col + c;
            var cursor = this.mBg.getCursor();
            var buffer = this.mBg.buffer();

            if (this.mConfig.hasCell(cell)) {
                if (this.mConfig.hasCell(cell - 1)) {
                    var offset = this.mConfig.getOffset(cell - 1);
                    cursor = this.mGridModel.appendCell(offset, 2, buffer, cursor);// 2 for all
                } else {
                    // no previous
                    var offset = this.mConfig.getOffset(cell);
                    cursor = this.mGridModel.appendCell(offset, 1, buffer, cursor);// 1 for light
                }
            } else {
                if (this.mConfig.hasCell(cell - 1)) {
                    var offset = this.mConfig.getOffset(cell - 1);
                    cursor = this.mGridModel.appendCell(offset, 0, buffer, cursor);// 0 for dark
                }
            }

            this.mBg.setCursor(cursor);
        }
    }

    this.mBg.upload();
};

/**
 * find edge in the right hand of current cell
 *
 * return edge id
 * @returns {number}
 */
Map.prototype.nextEdge = function () {
    var next = 2;
    var cell = this.mCell;
    for (var i = 0, orders = this.mNearBy[this.mEdge], l = orders.length; i < l; i++) {
        if (this.mConfig.hasCell(this.mCell + orders[i])) {
            cell = this.mCell + orders[i];
            next = i;
            break;
        }
    }

    this.mEdge = (this.mEdge + this.mEdgeOffset[next]) % 4;
    this.mCell = cell;
    return cell * 4 + this.mEdge;
};
Map.prototype.fillLines = function () {
    this.mLines.setCursor(0);

    this.mStartEdge = this.mConfig.getStartEdge();
    while (this.mStartEdge != -1) {
        this.mEdge = this.mStartEdge % 4;
        this.mCell = (this.mStartEdge - this.mEdge) >> 2;
        this.startAppend();

        var edgeId = this.mStartEdge;
        do {
            edgeId = this.nextEdge();
            this.appendEdge();
            this.mConfig.edgeDiscount(this.mCell, this.mEdge);
        } while (edgeId != this.mStartEdge);

        // draw other other areas's outline
        this.mStartEdge = this.mConfig.getStartEdge();
    }

    this.mLines.upload();
};
Map.prototype.initEdgePosition = function () {
    this.mEdgeOffset = [3, 0, 1];// righttop, right, self
    var nearby = this.mNearBy = [];
    var col = this.mConfig.getCol();
    nearby.push([-col - 1, -col]);// left:righttop, right, self
    nearby.push([-col + 1, 1]);// top
    nearby.push([col + 1, col]);// right
    nearby.push([col - 1, -1]);// bottom
};
Map.prototype.startAppend = function () {
    this.mPreviousEdge = this.mEdge;
};
Map.prototype.appendEdge = function () {
    var offset = this.mConfig.getOffset(this.mCell);
    var cursor = this.mLines.getCursor();
    var buffer = this.mLines.buffer();

    if (this.mEdge == this.mPreviousEdge) {
        cursor = this.mOutline.appendLine(offset, this.mEdge, buffer, cursor);
    } else {
        if (this.mEdge == (this.mPreviousEdge + 1) % 4) {
            cursor = this.mOutline.appendOuter(offset, this.mEdge, buffer, cursor);
        } else {
            cursor = this.mOutline.appendInner(offset, this.mEdge, buffer, cursor);
        }
        this.mPreviousEdge = this.mEdge;
    }

    this.mLines.setCursor(cursor);
};

module.exports = Map;